package com.pocket.insight.base.config.mybatis.interceptor;

import com.pocket.insight.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;

/**
 * 描述：
 *
 * @author shuize
 * @create 2024/12/26
 */
@Slf4j
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class ModelCommonInfoInterceptor implements Interceptor {

    private static final String SET_IS_DELETE  = "setIsDelete";
    private static final String SET_VERSION    = "setVersion";
    private static final String SET_CREATED_BY = "setCreatedBy";
    private static final String SET_CREATED_AT = "setCreatedAt";
    private static final String SET_UPDATED_BY = "setUpdatedBy";
    private static final String SET_UPDATED_AT = "setUpdatedAt";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        if (ms.getSqlCommandType().equals(SqlCommandType.INSERT)) {
            fillAddCommonInfo(parameter);
        } else if (ms.getSqlCommandType().equals(SqlCommandType.UPDATE)) {
            fillUpdateCommonInfo(parameter);
        }
        return invocation.proceed();
    }

    public void fillAddCommonInfo(Object object) throws InvocationTargetException, IllegalAccessException {
        if (object == null) {
            return;
        }
        User user = new User(1l);
        Method setCreatedBy = ReflectionUtils.findMethod(object.getClass(), SET_CREATED_BY, Long.class);
        Method setCreatedAt = ReflectionUtils.findMethod(object.getClass(), SET_CREATED_AT, LocalDateTime.class);
        Method setIsDelete = ReflectionUtils.findMethod(object.getClass(), SET_IS_DELETE, Boolean.class);
        Method setVersion = ReflectionUtils.findMethod(object.getClass(), SET_VERSION, Long.class);
        invoke(setCreatedAt, object, LocalDateTime.now());
        if (user != null) {
            invoke(setCreatedBy, object, user.getId());
        }
        invoke(setIsDelete, object, false);
        invoke(setVersion, object, 1l);
        fillUpdateCommonInfo(object);
    }

    public void fillUpdateCommonInfo(Object object) throws InvocationTargetException, IllegalAccessException {
        if (object != null) {
            User user = new User(1l);
            Method setUpdatedBy = ReflectionUtils.findMethod(object.getClass(), SET_UPDATED_BY, Long.class);
            Method setUpdatedAt = ReflectionUtils.findMethod(object.getClass(), SET_UPDATED_AT, LocalDateTime.class);
            invoke(setUpdatedAt, object, LocalDateTime.now());
            if (user != null) {
                invoke(setUpdatedBy, object, user.getId());
            }
        }
    }

    public void invoke(Method method, Object obj, Object... args) throws InvocationTargetException, IllegalAccessException {
        if (method == null) {
            return;
        }
        if (!Modifier.isPublic(method.getModifiers()) && !method.canAccess(obj)) {
            try {
                method.setAccessible(true);
            } catch (SecurityException e) {
                log.warn(e.getMessage(), e);
                return;
            }
        }
        method.invoke(obj, args);
    }
}
